#include "reseau.h"
#include "client_reseau.h"
#include "temps/minuterie.h"

/**
 * Connecte à un serveur donné par TCP, et par UDP avec un protocole de
 * confirmation.
 * Connexion bloquante.
 */
client_reseau::client_reseau(std::string nom_serveur, int port_serveur) : emetteur_(0), recepteur_(0) {
    // Résolution du nom du serveur
    if(SDLNet_ResolveHost(&adresse_serveur_, nom_serveur.c_str(), port_serveur) < 0)
        throw une_erreur<resolution_nom_hote>();
	
    // Connexion au serveur par TCP
	chaussette_tcp_ = SDLNet_TCP_Open(&adresse_serveur_);
    if(!chaussette_tcp_)
        throw une_erreur<ouverture_socket>();
		
	chaussette_udp_ = SDLNet_UDP_Open(port_serveur);
    if(!chaussette_udp_) {
		detruire();
        throw une_erreur<ouverture_socket>();
	}
	        
    // Ouverture d'une paire de chaussettes
    paire_de_chaussettes_ = SDLNet_AllocSocketSet(1);
    if(!paire_de_chaussettes_) {
        // Si erreur...
		detruire();
        throw une_erreur<ouverture_socket>();
    }
    
    // Composition de la paire de chaussettes
    if(SDLNet_TCP_AddSocket(paire_de_chaussettes_, chaussette_tcp_) < 0) {
        detruire();
        throw une_erreur<ouverture_socket>();
    }
	
	// On démarre le récepteur tout de suite vu qu'on a déjà ouvert un port UDP et un TCP.
	recepteur_ = new recepteur_reseau(chaussette_tcp_, chaussette_udp_, paire_de_chaussettes_);
	
	// On veut ici recevoir le port UDP que le serveur se sera attribué.
	Uint16 port_srv = 0;
	{
		minuterie<> timeout(1000);
		while(!recepteur_->recevoir_port_udp(port_srv) && !timeout);
	}
	
	// Il arrive qu'on ne reçoive pas de réponse du serveur, c'est mauvais signe !
	if(port_srv == 0) {
		detruire();
		throw une_erreur<protocole_connexion>();
	}
	
	// On peut compléter les informations sur l'adresse et le port UDP du serveur.
	adresse_serveur_udp_.host = adresse_serveur_.host;
	adresse_serveur_udp_.port = port_srv;
    
    // Démarrage de l'émetteur grâce à ces nouvelles informations.
    emetteur_ = new emetteur_reseau(chaussette_tcp_, chaussette_udp_, adresse_serveur_udp_);
	
	// On envoie une confirmation et c'est fini.
	{
		char ack[2] = "C";
		minuterie<> timeout(100);
		do {
			emetteur_->envoyer_udp(ack, 2);
		} while(!timeout);
	}
}

/**
 * Appelé pour libérer les ressources allouées par le client.
 */
void client_reseau::detruire() {
	delete emetteur_;
	delete recepteur_;
	
    SDLNet_FreeSocketSet(paire_de_chaussettes_);
	if(chaussette_udp_)
		SDLNet_UDP_Close(chaussette_udp_);
	if(chaussette_tcp_)
		SDLNet_TCP_Close(chaussette_tcp_);
}
